package com.zqq.webcloud.httpTest;

import android.content.Context;
import android.content.res.AssetManager;
import android.os.AsyncTask;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * 用于进行网络请求的AsyncTask
 *
 * @author zqq on 2018/9/7
 */
public class NetworkAsyncTask extends AsyncTask<String, Integer, Map<String, Object>> {

    private setResponseListener listener;
    private Context context;

    public NetworkAsyncTask(Context context, setResponseListener listener) {
        this.context = context;
        this.listener = listener;
    }

    //NETWORK_GET表示发送GET请求
    public static final String NETWORK_GET = "NETWORK_GET";
    //NETWORK_POST_KEY_VALUE表示用POST发送键值对数据
    public static final String NETWORK_POST_KEY_VALUE = "NETWORK_POST_KEY_VALUE";
    //NETWORK_POST_XML表示用POST发送XML数据
    public static final String NETWORK_POST_XML = "NETWORK_POST_XML";
    //NETWORK_POST_JSON表示用POST发送JSON数据
    public static final String NETWORK_POST_JSON = "NETWORK_POST_JSON";


    @Override
    protected Map<String, Object> doInBackground(String... params) {
        Map<String, Object> result = new HashMap<>();
        URL url = null;
        HttpURLConnection conn = null;
        String requestHeader = null;    //请求头
        byte[] requestBody = null;      //请求体
        String responseHeader = null;   //响应头
        byte[] responseBody = null;     //响应体
        String action = params[0];

        try {
            //get请求
            if (NETWORK_GET.equals(action)) {
                url = new URL("https://leancloud.cn:443/1.1/classes/urls?limit=4");
                conn = (HttpURLConnection) url.openConnection();
                //HttpURLConnection默认就是用GET发送请求，所以下面的setRequestMethod可以省略
                conn.setRequestMethod("GET");
                //HttpURLConnection默认也支持从服务端读取结果流，所以下面的setDoInput也可以省略
                conn.setDoInput(true);
                //用setRequestProperty方法设置一个自定义的请求头:action，用于后端判断
                conn.setRequestProperty("action", NETWORK_GET);
                conn.setRequestProperty("X-LC-Id", "LiGEddCUfS94R9VBfpww9mWO-gzGzoHsz");
                conn.setRequestProperty("X-LC-Key", "1YVHJJqtSrU50RGK7fH7MT87");
                conn.setRequestProperty("Content-Type", "application/json");

                //禁用网络缓存,GET请求容易被缓存
                conn.setUseCaches(false);

                //获取请求头
                requestHeader = getReqeustHeader(conn);

                //在对各种参数配置完成后，通过调用connect方法建立TCP连接，但是并未真正获取数据
                //conn.connect()方法不必显式调用，当调用conn.getInputStream()方法时内部也会自动调用connect方法
//                conn.connect();
                //调用getInputStream方法后，服务端才会收到请求，并阻塞式地接收服务端返回的数据
                InputStream is = conn.getInputStream();

                //将InputStream转换成byte数组,getBytesByInputStream会关闭输入流
                responseBody = getBytesByInputStream(is);

                //获取响应头
                responseHeader = getResponseHeader(conn);
            }
            //POST请求
            else if (NETWORK_POST_KEY_VALUE.equals(action)) {
                url = new URL("http://www.baidu.com");
                conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("POST");

                //调用conn.setDoOutput()方法以显式开启请求体
                //如果想用POST发送请求体，那么需要调用setDoOutput方法，将其设置为true。
                conn.setDoOutput(true);

                //用setRequestProperty方法设置一个自定义的请求头:action，由于后端判断
                conn.setRequestProperty("action", NETWORK_POST_KEY_VALUE);

                //获取请求头
                requestHeader = getReqeustHeader(conn);
                //获取conn的输出流
                OutputStream os = conn.getOutputStream();
                //获取两个键值对name=**和age=27的字节数组，将该字节数组作为请求体
                requestBody = new String("name=**&age=27").getBytes("UTF-8");

                //将请求体写入到conn的输出流中
                os.write(requestBody);
                //记得调用输出流的flush方法
                os.flush();
                //关闭输出流
                os.close();

                //当调用getInputStream方法时才真正将请求体数据上传至服务器
                InputStream is = conn.getInputStream();
                //获得响应体的字节数组
                responseBody = getBytesByInputStream(is);

                //获得响应头
                responseHeader = getResponseHeader(conn);
            }
            //用POST发送XML数据
            else if (NETWORK_POST_XML.equals(action)) {
                url = new URL("http://www.baidu.com");
                conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("POST");
                conn.setDoOutput(true);
                conn.setRequestProperty("action", NETWORK_POST_XML);
                requestHeader = getReqeustHeader(conn);     //获取请求头

                //获取conn的输出流
                OutputStream os = conn.getOutputStream();
                //读取assets目录下的person.xml文件，将其字节数组作为请求体
                requestBody = getBytesFromAssets("person.xml");
                //将请求体写入到conn的输出流中
                os.write(requestBody);
                //记得调用输出流的flush方法
                os.flush();
                os.close();

                //当调用getInputStream方法时才真正将请求体上传至服务器
                InputStream is = conn.getInputStream();
                responseBody = getBytesByInputStream(is);

                responseHeader = getResponseHeader(conn);
            }
            //用post发送json数据
            else if (NETWORK_POST_JSON.equals(action)) {
                url = new URL("http://www.baidu.com");
                conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("POST");
                conn.setDoOutput(true);

                conn.setRequestProperty("action", NETWORK_POST_JSON);

                requestHeader = getReqeustHeader(conn);

                //获取conn的输出流
                OutputStream os = conn.getOutputStream();
                //读取assets目录下的person.json文件，将其字节数组作为请求体
                requestBody = getBytesFromAssets("person.json");
                //将请求体写入到conn的输出流中
                os.write(requestBody);
                os.flush();
                os.close();

                //当调用getInputStream方法时才真正将请求体数据上传至服务器
                InputStream is = conn.getInputStream();
                responseBody = getBytesByInputStream(is);
                responseHeader = getResponseHeader(conn);
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //最后将conn断开连接
            if (conn != null) {
                conn.disconnect();
            }
        }

        result.put("url", url.toString());
        result.put("action", action);
        result.put("requestHeader", requestHeader);
        result.put("requestBody", requestBody);
        result.put("responseHeader", responseHeader);
        result.put("responseBody", responseBody);

        return result;
    }

    @Override
    protected void onPostExecute(Map<String, Object> result) {
        super.onPostExecute(result);
        String url = (String) result.get("url");
        String action = (String) result.get("action");//http请求的操作类型
        String requestHeader = (String) result.get("requestHeader");
        byte[] requestBody = (byte[]) result.get("requestBody");
        String responseHeader = (String) result.get("responseHeader");
        byte[] responseBody = (byte[]) result.get("responseBody");

        //1.显示Url---显示请求头---显示响应头
        if (requestHeader != null && responseHeader != null) {
            listener.setUrlHeader(url, requestHeader, responseHeader);
        }
        //2.显示请求体
        if (requestBody != null) {
            try {
                String request = new String(requestBody, "UTF-8");
                listener.setBody(request);

            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        //3.显示响应体
        if (NETWORK_GET.equals(action)) {
            //根据字节数组构建UTF-8字符串
            String response = getStringByBytes(responseBody);
            listener.setResponse(response);
        } else if (NETWORK_POST_KEY_VALUE.equals(action)) {
            String response = getStringByBytes(responseBody);
            listener.setResponse(response);

        } else if (NETWORK_POST_XML.equals(action)) {
            //将表示xml的字节数组进行解析
            String response = parseXmlResultByBytes(responseBody);
            listener.setResponse(response);
        } else if (NETWORK_POST_JSON.equals(action)) {
            //将表示json的字节数组进行解析
            String response = parseJsonResultByBytes(responseBody);
            listener.setResponse(response);
        }
    }


    /**
     * 1.读取请求头
     * 需要注意的是getRequestProperty方法执行时，客户端和服务器之间必须还未建立TCP连接，
     * 即还没有调用connect方法，在connected之后执行getRequestProperty会抛出异常
     */
    private String getReqeustHeader(HttpURLConnection conn) {
        //https://github.com/square/okhttp/tree/master/okhttp-urlconnection
        Map<String, List<String>> requestHeaderMap = conn.getRequestProperties();
        Iterator<String> requestHeaderIterator = requestHeaderMap.keySet().iterator();
        StringBuilder sb = new StringBuilder();//速度快，不安全
        while (requestHeaderIterator.hasNext()) {
            String requestHeaderKey = requestHeaderIterator.next();
            String requestHeaderValue = conn.getRequestProperty(requestHeaderKey);
            sb.append(requestHeaderKey);
            sb.append(":");
            sb.append(requestHeaderValue);
            sb.append("\n");
        }
        return sb.toString();
    }

    /**
     * 2.读取响应头
     */
    private String getResponseHeader(HttpURLConnection conn) {
        Map<String, List<String>> responseHeaderMap = conn.getHeaderFields();
        int size = responseHeaderMap.size();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < size; i++) {
            String responseHeaderKey = conn.getHeaderFieldKey(i);
            String responseHeaderValue = conn.getHeaderField(i);
            sb.append(responseHeaderKey);
            sb.append(";");
            sb.append(responseHeaderValue);
            sb.append("\n");
        }
        return sb.toString();
    }

    /**
     * 3.获取byte响应数据:从InputStream中读取数据，转换成byte数组，最后关闭InputStream
     * read方法是阻塞式的，每read一次，其实就是从服务器上下载一部分数据
     */
    private byte[] getBytesByInputStream(InputStream is) {
        byte[] bytes = null;
        BufferedInputStream bis = new BufferedInputStream(is);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        BufferedOutputStream bos = new BufferedOutputStream(baos);
        byte[] buffer = new byte[1024 * 8];
        int len = 0;

        try {
            while ((len = bis.read(buffer)) > 0) {
                baos.write(buffer, 0, len);
            }
            bos.flush();
            bytes = baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                bos.close();
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return bytes;
    }

    /**
     * 4.根据文件名，从asserts目录中读取文件的字节数组
     */
    private byte[] getBytesFromAssets(String fileName) {
        byte[] bytes = null;
        AssetManager assetManager = context.getAssets();
        InputStream is = null;

        try {
            is = assetManager.open(fileName);
            bytes = getBytesByInputStream(is);

        } catch (IOException e) {
            e.printStackTrace();
        }
        return bytes;
    }


    /**
     * 5.根据字节数组构建UTF-8字符串
     */
    private String getStringByBytes(byte[] bytes) {
        String str = "";

        try {
            str = new String(bytes, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return str;
    }


    /**
     * 6.将表示xml的字节数组进行解析
     */
    private String parseXmlResultByBytes(byte[] bytes) {
        InputStream is = new ByteArrayInputStream(bytes);
        StringBuilder sb = new StringBuilder();

        List<Person> persons = XmlParser.parse(is);
        for (Person person : persons) {
            sb.append(person.toString()).append("\n");
        }
        return sb.toString();
    }


    /**
     * 7.将表示json的字节数组进行解析
     */
    private String parseJsonResultByBytes(byte[] bytes) {
        String jsonString = getStringByBytes(bytes);
        List<Person> persons = JsonParser.parse(jsonString);
        StringBuilder sb = new StringBuilder();
        for (Person person : persons) {
            sb.append(person.toString()).append("\n");
        }
        return sb.toString();
    }


    interface setResponseListener {
        void setUrlHeader(String url, String requestHeader, String responseHeader);

        void setBody(String body);

        void setResponse(String response);
    }


}
